﻿using System;
using System.IO;
using System.Reflection;
using System.Globalization;
using System.Resources;
using System.Collections.Generic;
using System.Threading;

namespace SBPweb.Forge.Modules.Resources
{
	/// <summary>
	/// Class of ResourceHandler
	/// </summary>
    public class ResourceHandler
	{

		#region Private fields

        /// <summary>
        /// Available cultures supported by the ResourceHandler.
        /// </summary>
		private List<CultureInfo> cultures;

		#endregion

		#region Protected fields

        /// <summary>
        /// The data structure containing the available ResourceSets for specific cultures.
        /// </summary>
		protected Dictionary<CultureInfo, List<ResourceSet>> pResourceSets = new Dictionary<CultureInfo, List<ResourceSet>>();

        #endregion

		#region Constructor logic

        /// <summary>
        /// Instantiates a ResourceHandler and registers its available ResourceSets into the pResourceSets data structure.
        /// </summary>
        /// <param name="type">
        /// A type that is defined in an assembly; having satellite assemblies and/or containing manifest resources to read from.
        /// </param>
        /// <param name="cultures">
        /// A list of cultures supported by the assembly.
        /// <paramref name="System.Globalization.CultureInfo"/>
        /// </param>
		public ResourceHandler(Type type, List<CultureInfo> cultures)
        {
			this.cultures = cultures;

            foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures | CultureTypes.NeutralCultures))
            {
                if (cultures.Contains(ci))
                {
                    try
                    {
                        Assembly satellite = type.Assembly.GetSatelliteAssembly(ci);
                        foreach (string resName in satellite.GetManifestResourceNames())
                        {
                            Stream s = satellite.GetManifestResourceStream(resName);
                            ResourceSet rs = new ResourceSet(s);
                            if (pResourceSets.ContainsKey(ci))
                            {
                                pResourceSets[ci].Add(rs);
                            }
                            else
                            {
                                pResourceSets.Add(ci, new List<ResourceSet>(new ResourceSet[] { rs }));
                            }
                        }
                    }
                    catch (FileNotFoundException)
                    {
                    }
                }
            }
        }

        #endregion

        #region Property accessors

        /// <summary>
        /// Gets the list of available cultures supported by the ResourceHandler.
        /// </summary>
        public List<CultureInfo> Cultures
        {
            get
            {
                return cultures;
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when the SetCurrentCulture method was invoked.
        /// </summary>
        public static event EventHandler<CultureChangeEventArgs> CultureChanged;

        #endregion

        #region Public methods

        /// <summary>
        /// Gets a String with a given resource key using the CurrentUICulture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <returns>
        /// The string of with the given name.
        /// </returns>
        public string GetString(string name)
        {
            return GetString(name, CultureInfo.CurrentUICulture);
        }

        /// <summary>
        /// Gets a String with a given resource key using a given culture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <param name="culture">
        /// The culture used to get the value from the ResourceSet.
        /// </param>
        /// <returns>
        /// The string of with the given name.
        /// </returns>
        public string GetString(string name, CultureInfo culture)
        {
            if (!pResourceSets.ContainsKey(culture))
            {
                return null;
            }
            string ret = null;
            foreach (ResourceSet rs in pResourceSets[culture])
            {
                ret = rs.GetString(name);
                if (ret != null)
                {
                    return ret;
                }
            }
            return ret;
        }

        /// <summary>
        /// Gets an Object with a given resource key using the CurrentUICulture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <returns>
        /// The object with the given name.
        /// </returns>
        public object GetObject(string name)
        {
            return GetObject<object>(name, CultureInfo.CurrentUICulture);
        }

        /// <summary>
        /// Gets an object of type T with a given resource key using the CurrentUICulture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <returns>
        /// The T with the given name.
        /// </returns>
        public T GetObject<T>(string name)
        {
            return GetObject<T>(name, CultureInfo.CurrentUICulture);
        }

        /// <summary>
        /// Gets an Object with a given resource key using a given culture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <param name="culture">
        /// The culture used to get the value from the ResourceSet.
        /// </param>
        /// <returns>
        /// The object with the given name.
        /// </returns>
        public object GetObject(string name, CultureInfo culture)
        {
            return GetObject<object>(name, culture);
        }

        /// <summary>
        /// Gets an object of type T with a given resource key using a given culture.
        /// </summary>
        /// <param name="name">
        /// The resource name used in the ResourceSet.
        /// </param>
        /// <param name="culture">
        /// The culture used to get the value from the ResourceSet.
        /// </param>
        /// <returns>
        /// The T with the given name.
        /// </returns>
        public T GetObject<T>(string name, CultureInfo culture)
        {
            T ret = default(T);
            if (!pResourceSets.ContainsKey(culture))
            {
                return ret;
            }
            foreach (ResourceSet rs in pResourceSets[culture])
            {
                object o = rs.GetObject(name);
                if (o != null)
                {
                    return (T)o;
                }
            }
            return ret;
        }

        #endregion

        #region Public static helper functions

        /// <summary>
        /// Retrieves an assembly's custom attribute's value by a given generic type.
        /// </summary>
        /// <typeparam name="T">
        /// The Type of the Attribute to get back. 
        /// </typeparam>
        /// <param name="asm">
        /// The Assembly having the Attribute.
        /// </param>
        /// <returns>
        /// The attribute's value as an object.
        /// <value>
        /// If there is no such attribute, it will be a null.
        /// </value>
        /// </returns>
        public static object GetAssemblyCustomAttribute<T>(Assembly asm) where T : Attribute
        {
            foreach (CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(asm))
            {
                if (cad.Constructor.DeclaringType == typeof(T))
                {
                    return (cad.ConstructorArguments[0].Value);
                }
            }
            return null;
        }

        /// <summary>
        /// Sets the CurrentCulture and CurrentUICulture property of the CurrentThread.
        /// </summary>
        /// <param name="culture">The culture to set the current culture to.</param>
        public static void SetCurrentCulture(CultureInfo culture)
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
            if (CultureChanged != null)
            {
                CultureChanged(Thread.CurrentThread, new CultureChangeEventArgs(culture));
            }
        }

        #endregion

    }
}
